# -*- coding: utf_8 -*-

from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
import pmcore.basecom


""" This is a component that implements the camera, used for 
strategic games. 
Shematic representation (j1,j2,j3 - joints):

    j2+-------+j3(camera)
      |
      |
      |
----j1+(ground)--------------
    
The component uses the following settings:
start_height        - starting distance between j1 and j2
start_distance      - starting distance between j2 and j3
start_heading_angle - starting angle of j1 vertical axis
start_pitch_angle   - starting angle of j2 "from left to right axis"
max_pitch_angle     - maximum pitch angle
min_pitch_angle     - minimum pitch angle
"""
class PMStCamComponent (pmcore.basecom.BaseComponent):
    name = "Strategy camera handler"
    short = "stCamH"
    version = "1.2"
    author = "Ninth"
    siteURL = "www.panda3d.org.ru"
    cHandler = None
    
    def __init__(self):
        pmcore.basecom.BaseComponent.__init__(self)
        self.provides = ({"name":"strategy-camera-set-parametres",
                        "value":self.set_cam_parametres,
                        "version":"0.1"},)
    def after_direct_import(self):
        base.disableMouse()
        self.cHandler = CameraHandler()
        if self.settings.has_key("start_height"):
            self.cHandler.j2.setZ(float(self.settings["start_height"]))
            
        if self.settings.has_key("start_distance"):
            self.cHandler.j3.setY(-float(self.settings["start_distance"]))
            
        if self.settings.has_key("start_heading_angle"):
            self.cHandler.j2.setH(float(self.settings["start_heading_angle"]))
            
        if self.settings.has_key("start_pitch_angle"):
            self.cHandler.j2.setP(-float(self.settings["start_pitch_angle"]))
            
        if self.settings.has_key("max_pitch_angle"):
            self.cHandler.maxP = float(self.settings["max_pitch_angle"])
            
        if self.settings.has_key("min_pitch_angle"):
            self.cHandler.minP = float(self.settings["min_pitch_angle"])

    def set_cam_parametres(self,height = None, distance = None, heading = None, pitch = None):
        if self.cHandler:
            if height: self.cHandler.j2.setZ(height)
            if distance: self.cHandler.j3.setY(-distance)
            if heading: self.cHandler.j2.setH(heading)
            if pitch: self.cHandler.j2.setP(pitch)
        else:
            print "ERROR:Strategy camera handler: not ready."

class CameraHandler(DirectObject):
    maxP = 80;
    minP = 10
    def __init__(self):
        self.mx,self.my=0,0
        self.prevPoint=Vec3(0,0,0)
        self.prevJPoint=Vec3(0,0,0)
        self.dragging=[False,False]
        self.hc=heightChecker()
        
        self.j1 = render.attachNewNode('cam_j1')
        self.j2 = self.j1.attachNewNode('cam_j2')
        self.j2.setZ(5)
        self.j3 = self.j2.attachNewNode('cam_j3')
        self.j3.setY(-40)
        
        self.accept("mouse2",self.drag,[False,True])
        self.accept("mouse2-up",self.drag,[False,False])
        self.accept("mouse3",self.drag,[True,False])
        self.accept("mouse3-up",self.drag,[False,False])
        self.accept("wheel_up", self.adjust_cam_dist,[0.9])
        self.accept("wheel_down", self.adjust_cam_dist,[1.1])  
        
        he = get_service("event-translator-has-event")
        if he:
            if he("mouse2"):
                print "mouse2"
                add_listener("stratacam-mouse2","mouse2",self.drag,[False,True])
            if he("mouse2-up"):
                print "mouse2-up"
                add_listener("stratacam-mouse2-up","mouse2-up",self.drag,[False,False])
            if he("mouse3"):
                print "mouse3"
                add_listener("stratacam-mouse3","mouse3",self.drag,[True,False])
            if he("mouse3-up"):
                print "mouse3-up"
                add_listener("stratacam-mouse3-up","mouse3-up",self.drag,[False,True])
            if he("wheel_up"):
                add_listener("stratacam-wheel_up","wheel_up",self.adjust_cam_dist,[0.9])
            if he("wheel_down"):
                add_listener("stratacam-wheel_down","wheel_down",self.adjust_cam_dist,[1.1])
                
        taskMgr.add(self.drag_task,'drag_task')   

    def turn_camera(self,tx,ty):
        self.j1.setH(self.j1.getH()+tx)
        self.j2.setP(self.j2.getP()-ty)
        if self.j2.getP()<-self.maxP:
            self.j2.setP(-self.maxP)
        if self.j2.getP()>-self.minP:
            self.j2.setP(-self.minP)

    def drag(self,left,right):
        self.dragging=[left,right]
            
    def adjust_cam_dist(self,aspect):
        self.j3.setY(self.j3.getY()*aspect)

    def drag_task(self,task):
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse() 
            if self.dragging[1]:
                self.turn_camera((self.mx-mpos.getX())*100,(self.my-mpos.getY())*100)
            elif self.dragging[0]:
                self.j1.setPos(self.j1,Vec3(-0.5*(self.mx-mpos.getX())*self.j3.getY(),
                                            -0.5*(self.my-mpos.getY())*self.j3.getY(),0))
                pass
            self.mx=mpos.getX()
            self.my=mpos.getY()
        d=self.hc.getHeight(render,self.j1.getPos())-self.j1.getZ()
        self.j1.setZ(self.j1.getZ()+d*0.1)
        deltaZ=self.j3.getZ(render)-(self.hc.getHeight(render,self.j3.getPos(render))+5)
        if deltaZ<0:
            self.turn_camera(0,-deltaZ)
        vDir=Vec3(self.j3.getPos(render))-Vec3(base.camera.getPos(render))
        vDir=vDir*0.2
        base.camera.setPos(Vec3(base.camera.getPos())+vDir)
        base.camera.lookAt(self.j1.getPos(render))
        return task.cont   


class heightChecker():
    def __init__(self):
        self.picker         = CollisionTraverser()            
        self.pickerQ        = CollisionHandlerQueue()         
        pickerCollN         = CollisionNode('heightChecker')       
        self.pickerNode     = render.attachNewNode(pickerCollN) 
        pickerCollN.setFromCollideMask(BitMask32.bit(1))         
        pickerCollN.setIntoCollideMask(BitMask32.allOff())         
        self.pickerRay      = CollisionRay(0,0,300,0,0,-1)                
        pickerCollN.addSolid(self.pickerRay)      
        self.picker.addCollider(self.pickerNode, self.pickerQ)
    def getHeight(self,obj,pos):
        res=0
        self.pickerNode.setPos(pos)
        self.picker.traverse(obj)
        if self.pickerQ.getNumEntries() > 0:
            self.pickerQ.sortEntries()
            res=self.pickerQ.getEntry(0).getSurfacePoint(render).getZ()
        return res
    def getNormal(self,obj,pos):
        res=Vec3(0,0,1)
        self.pickerNode.setPos(pos)
        self.picker.traverse(obj)
        if self.pickerQ.getNumEntries() > 0:
            self.pickerQ.sortEntries()
            res=Vec3(self.pickerQ.getEntry(0).getSurfaceNormal(obj))
        return res
